/*
 * OperatorBasedNeighbourhood.cpp
 *
 *  Created on: Jan 16, 2011
 *      Author: LyonsDesktop
 */

#include "OperatorBasedNeighbourhood.h"
#include "stdlib.h"
#ifdef FRAMEWORKMEMORYTRACKER
#  include "../../Extern.h"
#  define new new (__FILE__, __LINE__)
#endif

using namespace CLEVER::RegionDiscovery;

using namespace std;

std::vector<std::vector<int> > OperatorBasedNeighbourhood::FindNeighbours(std::vector<int> & CurReps, std::vector<int> & NonReps, int p)
{
	vector<vector<int> > neighbours;

	if (!CheckParameters(CurReps, NonReps, p)) {
		double raseError = 7 / 0;
	}
	for (int iN = 0; iN < p; iN++) {
		vector<int> cur_reps = vector<int>(CurReps);
		vector<int> cur_nreps = vector<int>(NonReps);
		vector<int> new_n;

		for (int iOp = 0; iOp < neighbourhood_size; iOp++) {
			Operator cur = SelectOperator(ops, op_prob);

			if (cur == OP_Insert) {
				new_n = Insert(cur_reps, cur_nreps);
			} else if (cur == OP_Delete) {
				new_n = Delete(cur_reps, cur_nreps);
			} else if (cur == OP_Replace) {
				new_n = Replace(cur_reps, cur_nreps);
			}
			cur_reps = new_n;

		}
		neighbours.push_back(new_n);
	}

	return neighbours;
}


bool OperatorBasedNeighbourhood::IsRedundancy(std::vector<int> & rlist)
{
	for (unsigned int i = 0; i < rlist.size() - 1; i++) {
		int cur = rlist[i];
		for (unsigned int j = 0; j < rlist.size(); j++) {
			if (rlist[j] == cur) {
				return true;
			}
		}
	}
	return false;
}


std::vector<int> OperatorBasedNeighbourhood::Insert(std::vector<int> & CurReps, std::vector<int> & NonReps)
{
	vector<int> uneighbour = vector<int>(CurReps);
	int newrep = rand() % NonReps.size();
	uneighbour.push_back(NonReps[newrep]);
	NonReps.erase(NonReps.begin() + newrep);
	return uneighbour;
}


std::vector<int> OperatorBasedNeighbourhood::Delete(std::vector<int> & CurReps, std::vector<int> & NonReps)
{
	vector<int> uneighbour = vector<int>(CurReps);
	if (CurReps.size() > 1) {
		int newrep = rand() % uneighbour.size();
		NonReps.push_back(uneighbour[newrep]);
		uneighbour.erase(uneighbour.begin() + newrep);
	}
	return uneighbour;
}


std::vector<int> OperatorBasedNeighbourhood::Replace(std::vector<int> & CurReps, std::vector<int> & NonReps)
{
	vector<int> uneighbour = vector<int>(CurReps);
	int a = rand() % uneighbour.size();
	int b = rand() % NonReps.size();

	int t = uneighbour[a];
	uneighbour[a] = NonReps[b];
	NonReps[b] = t;

	return uneighbour;
}


OperatorBasedNeighbourhood::Operator OperatorBasedNeighbourhood::SelectOperator(std::vector<Operator> & ops, std::vector<double> & op_prob)
{
	double r = rand() / ((double)RAND_MAX);
	double prob = 0;

	for (unsigned int i = 0; i < ops.size(); i++) {
		if (r >= prob && r < (op_prob[i] + prob)) {
			return ops[i];
		}
		prob += op_prob[i];
	}
	return ops[0];
}


bool OperatorBasedNeighbourhood::CheckParameters(std::vector<int> & CurReps, std::vector<int> & NonReps, int p)
{
	return op_prob.size() > 0 && ops.size() > 0 && neighbourhood_size > 0 && op_prob.size() == ops.size() &&
	       CheckValidProbs(op_prob) && CheckValidOps(ops) &&
	       CurReps.size() > 0 && NonReps.size() > 0 && p > 0;
}


bool OperatorBasedNeighbourhood::CheckValidProbs(std::vector<double> & op_prob)
{
	double total = 0;

	for (unsigned int i = 0; i < op_prob.size(); i++) {
		if (op_prob[i] < 0.0) {
			return false;
		}

		total += op_prob[i];
	}

	return abs(total - 1.0) < 0.000001f;
}


bool OperatorBasedNeighbourhood::CheckValidOps(std::vector<Operator> & ops)
{
	if (ops.size() > 3) {
		return false;
	}
	for (unsigned int i = 0; i < ops.size(); i++) {
		for (unsigned int j = i + 1; j < ops.size(); j++) {
			if (ops[i] == ops[j]) {
				return false;
			}
		}
	}
	return true;
}


OperatorBasedNeighbourhood::OperatorBasedNeighbourhood(std::vector<Operator> & ops, std::vector<double> & op_prob, int neighbourhood_size)
	: ops(ops), op_prob(op_prob), neighbourhood_size(neighbourhood_size)
{
	if (!CheckValidProbs(op_prob) || !CheckValidOps(ops)) {
		//__debugbreak();
	}
}


